Laboratorio 4: El Pandas no muerde (act. II) 🐼

MDS7202: Laboratorio de Programación Científica para Ciencia de Datos

Cuerpo Docente:¶

  • Profesor: Pablo Badilla
  • Auxiliar: Ignacio Meza D.
  • Ayudante: Patricio Ortiz

Equipo: SUPER IMPORTANTE - notebooks sin nombre no serán revisados¶

  • Nombre de alumno 1: Sebastián Avendaño
  • Nombre de alumno 2: Felipe Urrutia

Link de repositorio de GitHub: https://github.com/abeldano/labs-mds7202¶

Temas a tratar¶

  • Manejo de datos tabulares usando pandas. En esta segunda parte se incluye adicionalmente agregaciones, concatenaciones, merge y trabajo con strings.
  • Visualizaciones interactivas de los datos con plotly.

Reglas:¶

  • Fecha de entrega: 5 de mayo (atrasos hasta el 8 de mayo, 1 punto de descuento por día)
  • Grupos de máximo 2 personas
  • Ausentes deberán realizar la actividad solos.
  • Cualquier duda fuera del horario de clases al foro. Mensajes al equipo docente serán respondidos por este medio.
  • Prohibidas las copias.
  • Pueden usar cualquer matrial del curso que estimen conveniente.

Objetivos principales del laboratorio¶

  • Aplicar y aprovechar las ventajas que nos ofrece la libreria pandas.
  • Utilizar plotly para obtener información gráfica del dataset.
  • Aplicar el Análisis Exploratorio de Datos a un caso en particular.

Nota: El laboratorio deberá ser desarrollado sin el uso indiscriminado de iteradores nativos de python (aka "for", "while"). La idea es que aprendan a exprimir al máximo las funciones optimizadas que nos entrega pandas, las cuales vale mencionar, son bastante más eficientes que los iteradores nativos sobre DataFrames.

Importamos librerias utiles 😸¶

In [4]:
# Libreria Core del lab.
import numpy as np
import pandas as pd 

from IPython.display import display

#Libreria para visualizar
#!pip install --upgrade plotly
import plotly.figure_factory as ff
import plotly.express as px

1. Rendimiento en Estudiantes 📚¶

Para este laboratorio deberán continuar el Análisis Exploratorio de datos sobre el conjunto students_grades.csv, el cual contiene una caracterización sobre el rendimiento y otros atributos de cada alumno de la Universidad de la Cachaña .

In [ ]:
# Si usted está utilizando Colabolatory le puede ser útil este código para cargar los archivos.
try:
    from google.colab import drive
    drive.mount("/content/drive")
    path = 'Dirección donde tiene los archivos en el Drive'
except: 
    print('Ignorando conexión drive-colab')
Ignorando conexión drive-colab

Carga de Datos [0.5 Puntos]¶

Ya finalizado en análisis inicial, ud. y su equipo le entregaron a Don Caguayo (rector de la Universidad de la Cachaña) tanto los resultados del análisis como también la base de datos limpia y lista para ser almacenada. Dada la ingente cantidad de los datos, el equipo de TI de la universidad resolvió separar el dataset en dos bases de datos distintas (lo que según argumentan ellos, permitiría hacer agregaciones de forma más eficiente).

Gracias a la excelente labor de ud. y su equipo en el análisis previo, el rector le solicita continuar el trabajo con una nueva batería de análisis. Por este motivo, la sección de TI les entrega nuevamente los datos. Sin embargo, argumentan que dada una escazes de personal, solo le entregarán dumps (copias) de cada base de datos y su equipo deberá unir las bases de datos. Los datos se encuentran en los siguiente archivos .json: students_grades_1.json y students_grades_2.json.

Por ende, ud. y su equipo deciden que la primera tarea se centrará en cargar estos datos y unirlos.

No se preocupe por la limpieza ni transformar el tipo de datos de las columnas, ni tampoco transformar a notas chilenas, recuerde que anteriormente ya se encargo de este tema.

In [5]:
df_grades = pd.concat([pd.read_json('students_grades_1.json'),pd.read_json('students_grades_2.json')],axis=0)

Resultado esperado:

In [7]:
df_grades
Out[7]:
names gender race/ethnicity parental level of education lunch test preparation course math score reading score writing score
0 Rita Courtney female group B some high school standard none 3.22 3.76 3.76
1 Charles Linstrom male group A bachelor's degree standard completed 5.80 5.68 5.86
2 Brian Young male group C some high school standard none 5.38 4.96 4.78
3 Howard Jimenez male group E some high school standard completed 5.86 5.50 5.56
4 Wayne Wilson male group B some high school standard completed 6.64 6.16 6.22
... ... ... ... ... ... ... ... ... ...
470 Richard Young male group D high school standard none 5.14 5.50 5.26
471 Wanda Russell female group B high school free/reduced completed 2.38 3.64 3.16
472 Marina Zeigler female group C bachelor's degree free/reduced completed 4.96 5.44 5.86
473 Laurie Carter female group B some high school standard completed 4.24 4.66 4.72
474 Amanda Perez female group A high school standard completed 5.08 5.80 5.56

875 rows × 9 columns

1.2- Análisis de Las Notas v2 [2 Punto por Gráficos + 0.5 respuesta]¶

Preocupado por la dificultad que representa el graficar correctamente las notas, el rector le solicita implementar distintas alternativas de visualización.

Para esto, genere un boxplot, un displot, un histograma con un gráfico marginal de caja y un histograma con el ramo como faceta de fila que permitan visualizar las notas.

Luego, responda las siguientes pregunta:

  1. ¿Existe una diferencia notable entre las notas?

  2. ¿Cuál de los gráficos mostrados cree que es adecuado para mostrarle al rector? ¿Y a los padres? ¿Y a un centro de estudios educativos? ¿Por qué?. Base sus respuestas en lo visto en la clase de visualizaciones como también en lo que usted y su equipo consideren correcto.

Hint: Para elaborar el histograma, puede que le sea de utilidad hacer un melt del DataFrame, dejando como variables los ramos y valores las notas. Por otra parte, visiten la documentación para generar los gráficos.

In [44]:
#melt del df
notas_m=df_grades.melt(id_vars=["names"])
In [46]:
notas_m[notas_m.variable.isin(materias)] 
Out[46]:
names variable value
4375 Rita Courtney math score 3.22
4376 Charles Linstrom math score 5.8
4377 Brian Young math score 5.38
4378 Howard Jimenez math score 5.86
4379 Wayne Wilson math score 6.64
... ... ... ...
6995 Richard Young writing score 5.26
6996 Wanda Russell writing score 3.16
6997 Marina Zeigler writing score 5.86
6998 Laurie Carter writing score 4.72
6999 Amanda Perez writing score 5.56

2625 rows × 3 columns

Gráfico de Caja:

In [68]:
# filtrado a solo mostrar notas de las materias
materias = ["math score","reading score","writing score"]
notas_f = notas_m[notas_m.variable.isin(materias)]
notas_f.rename(columns = {'value':'score'}, inplace = True)
notas_f.rename(columns = {'variable':'subject'}, inplace = True)

# boxplot
fig = px.box(notas_f, x="subject", y="score", color="subject") 
fig.show()

Distplot:

In [61]:
# use displot figurefactory
fig = ff.create_distplot([df_grades[m] for m in materias], materias, bin_size=.25, show_hist= False, show_rug=False)
fig.show()

Histograma con Boxplots:

In [69]:
fig = px.histogram(notas_f, x="score", color="subject", marginal="box", barmode="group", nbins=13)
fig.show()

Histograma con Faceta:

In [128]:
fig = px.histogram(notas_f, x="score", color="subject", facet_row="subject", nbins=13)
fig.show()

Resultados Esperados:

In [ ]:
 

Justifique:

  1. ¿Existe una diferencia notable entre las notas?

    Comparando las distintas materias, se puede notar que las distribuciones de las materias asociadas a contenidos de lenguaje, como lectura y escritura, son más similares entre sí, que con la distribución de las notas de matemática. Sin embargo, todas poseen la misma forma y no se diferencian tan marcadamente.

  2. ¿Cuál de los gráficos mostrados cree que es adecuado para mostrarle al rector? ¿Y a los padres? ¿Y a un centro de estudios educativos? ¿Por qué?. Base sus respuestas en lo visto en la clase de visualizaciones como también en lo que usted y su equipo consideren correcto.

    Al rector: Boxplot, porque el rector debe tomar decisiones en cuanto a la población de la escuela, por lo que es conveniente que el gráfico muestre una división por cuantiles.

    A los padres: Histograma con Facetas, dado que el interés de los padres es solamente saber la distribución de las notas, y los intervalos del histograma hace más fácil la lectura para ubicar, por ejemplo, el rango de la nota de su hijo.

    Centro de estudios educativos: Histograma con Boxplots, dado que el nivel de información que se entrega en este gráfico es más detallado.

2. Análisis por Nivel Educacional Etnia de los Padres¶

El rector, basado en su experiencia, cree fuertemente que el nivel educacional y la etnia de los padres influyen en las notas que obtienen sus hijos. Como científicos de datos, ud. y su equipo creen que deben encontrar evidencia para confirmar o refutar la hipótesis del rector.

Para esto, deciden generar dos análisis: una tabla de resumen por una parte y gráficos de caja por otro.

1.3.4 Tabla de Resumen [1 punto]¶

Para generar la tabla de resumen:

  • [ ] Calcular el promedio de las notas y guardarlo en una variable GPA (grade point average).
  • [ ] Hacer una simplificación a través de un mapeo (investigar el método map()) de la variable parental level of education según la siguiente conversión:

      some high school -> school
      some college -> school
      high school -> school
      bachelor's degree -> college
      associate's degree -> college
      master's degree -> postgraduate
    
    

    Los resultados de este mapeo deben ser guardados en la columna simple parental level of education.

  • [ ] Agregar según 2 niveles: race/ethnicity y simple parental level of education para obtener el promedio de las notas.
  • [ ] Agregar según 2 niveles: race/ethnicity y simple parental level of education para obtener un conteo de los alumnos en cada grupo y agregarlos como una nueva fila count.
  • [ ] Obtener el porcentaje de alumnos con respecto al total. Los porcentajes deben ser strings que contienen la frecuencia de cada grupo con respecto al total y deben ser terminados en '%'.

Utilizar la tabla de resultados esperados como guía para desarrollar este punto.

In [95]:
df_grades['GPA'] = df_grades[materias].mean(axis=1)
df_grades['simple parental level of education'] = df_grades["parental level of education"].map({
        "some high school" : "school",
        "some college" : "school",
        "high school" : "school",
        "bachelor's degree" : "college",
        "associate's degree" : "college",
        "master's degree" : "postgraduate"
        })
df_grades_map=df_grades.groupby(['race/ethnicity','simple parental level of education']).mean().round(decimals=2)
df_grades_map["count"]=df_grades.groupby(['race/ethnicity','simple parental level of education']).count()["GPA"]
df_grades_map["percentage"]=(100*df_grades_map["count"] / df_grades_map["count"].sum()).round(decimals=2).apply(lambda x: str(x)+" %")
df_grades_map
Out[95]:
math score reading score writing score GPA count percentage
race/ethnicity simple parental level of education
group A college 4.74 5.00 4.89 4.88 24 2.74 %
postgraduate 4.69 5.23 5.35 5.09 2 0.23 %
school 4.57 4.73 4.56 4.62 51 5.83 %
group B college 5.07 5.26 5.19 5.18 54 6.17 %
postgraduate 4.91 5.69 5.55 5.38 5 0.57 %
school 4.69 4.89 4.76 4.78 107 12.23 %
group C college 5.02 5.37 5.35 5.25 102 11.66 %
postgraduate 4.92 5.14 5.10 5.06 15 1.71 %
school 4.76 5.02 4.92 4.90 155 17.71 %
group D college 5.11 5.25 5.25 5.20 70 8.0 %
postgraduate 5.22 5.54 5.73 5.50 20 2.29 %
school 5.02 5.13 5.11 5.09 149 17.03 %
group E college 5.54 5.45 5.45 5.48 52 5.94 %
postgraduate 5.54 6.03 5.89 5.82 6 0.69 %
school 5.40 5.31 5.16 5.29 63 7.2 %

Resultado Esperado

race/ethnicity simple parental level of education math score reading score writing score GPA count percentage
0 group A college 4.74 5 4.89 4.88 24 2.74 %
1 postgraduate 4.69 5.23 5.35 5.09 2 0.23 %
2 school 4.57 4.73 4.56 4.62 51 5.83 %
3 group B college 5.07 5.26 5.19 5.18 54 6.17 %
4 postgraduate 4.91 5.69 5.55 5.38 5 0.57 %
5 school 4.69 4.89 4.76 4.78 107 12.23 %
6 group C college 5.02 5.37 5.35 5.25 102 11.66 %
7 postgraduate 4.92 5.14 5.1 5.06 15 1.71 %
8 school 4.76 5.02 4.92 4.9 155 17.71 %
9 group D college 5.11 5.25 5.25 5.2 70 8.0 %
10 postgraduate 5.22 5.54 5.73 5.5 20 2.29 %
11 school 5.02 5.13 5.11 5.09 149 17.03 %
12 group E college 5.54 5.45 5.45 5.48 52 5.94 %
13 postgraduate 5.54 6.03 5.89 5.82 6 0.69 %
14 school 5.4 5.31 5.16 5.29 63 7.2 %

Visualizaciones [0.5 Puntos]¶

Ahora, implemente un gráfico de caja en donde se muestre el GPA con respecto al nivel educacional y que la variable de color sea la etnicidad y luego comente.

In [108]:
#melt del df_grades
df_grades_map_melt=df_grades.melt(id_vars=['race/ethnicity','simple parental level of education'])

# filtrado a solo GPA
df_grades_map_melt_f = df_grades_map_melt[df_grades_map_melt.variable.isin(['GPA'])]

# boxplot
fig = px.box(df_grades_map_melt_f, x="simple parental level of education", y="value", color="race/ethnicity") 
fig.show()
  1. ¿Hay alguna diferencia entre los grupos graficados tanto para el nivel educacional de los padres como también para la etnicidad?
  2. ¿Este gráfico permite hacer facilmente un análisis conjunto de estas dos variables de forma sencilla?

Justifique:

  1. Sí existen diferencias. Es notorio que a mayor nivel educacional de los padres, se tiene un mayor rendimiento promedio y la dispersión en las notas disminuye. En cuanto a la etnicidad, en los distintos niveles educacionales de los padres se mantienen las diferencias en rendimiento. Por ejemplo, el grupo A siempre tiene bajo rendimiento promedio, mientras que el grupo B y grupo E, en cambio, tienen mayor cantidad de estudiantes de buen rendimiento.

  2. Sí lo permite, ya que se pueden desprender fácilmente conclusiones en ambas variables, nivel de educación de los padres y etnicidad. Sin embargo, depende de la principal diferencia que se quiere hacer notar. En este caso se aprecia más directamente las diferencias entre niveles de educación de padres. Si se quisiera hacer notar las diferencias entre etnias, se podría agrupar por etnia en vez de nivel de educación de padres.

3. Combinar Dataset [1 punto]¶

Mientras le notificaba por videollamada los resultados de sus descubrimientos a Don Caguayo, un exaltado practicante del area de TI entra a la reunión y les informa que ha encontrado una nueva base de datos que cuenta con las notas de dos asignaturas (en escala chilena): historia y ciencias. Para más remate, antes de huir, el practicante les cuenta que este dataframe lamentablemente contiene nuevamente los alumnos de los registros corruptos que ud. y su equipo filtraron en el análisis anterior.

El rector (evidentemente molesto por la situación) les ruega incluir estos datos (vaciados en el archivo other_grades.csv) al estudio original(students_grades.csv).

Para esto, carge el archivo other_grades.csv y busque la forma de unir ambos DataFrames, de tal manera que las columnas de history score y science score se anexen al final del DataFrame original. NO LIMPIE LOS DATOS, si no que explore los distintos tipos de merge para encontrar el mas situable para su situación (y así evitar buscar duplicados).

To-Do

  • [ ] Cargar el other_grades.csv
  • [ ] Unir df_grades con other_grades.csv usando outer join y explique el resultado.
  • [ ] Unir df_grades con other_grades.csv usando left join y explique el resultado.
  • [ ] Unir df_grades con other_grades.csv usando right join y explique el resultado.
  • [ ] Unir df_grades con other_grades.csv usando inner join y explique el resultado.
  • [ ] Defina cuál join es el que utilizará para generar el nuevo DataFrame.

Hint: Puede explicar los resultados del merge a través de la cantidad de filas resultantes y los valores que estas contienen.

In [7]:
df_other_grades = pd.read_csv('other_grades.csv')
print('Dimensiones de los distintos merges')
for h in ["outer","left","right","inner"]:
    print(f'{h}: {pd.merge(left=df_grades, right=df_other_grades, on="names", how=h).shape}')
Dimensiones de los distintos merges
outer: (1000, 11)
left: (875, 11)
right: (1000, 11)
inner: (875, 11)

Justificación:

  1. Outer: Incluye la unión de los dos conjuntos de datos, es decir, todos los nombres que están en "df_grades" y "other_grades". Por esto se obtienen 1000 filas, que son los nombres que comparten (875) más los duplicados de "other grades".
  2. Left Join: Este merge sólo dejará las filas cuyos nombres se encuentren en el dataframe definido para left, es decir, "df_grades", y por esto se obtienen sólo 875 filas.
  3. Right Join: Este merge dejará las filas cuyos nombres se encuentren en el dataframe definido para right, es decir, "other_grades", y por esto se obtienen 1000 filas.
  4. Inner Join: Este merge dejará las filas que se encuentren en ambos dataframes simultáneamente. Los nombres de "df_grades" se incluyen en los de "other_grades", debido a que en éste sólo se añaden duplicados (no nombres nuevos). Entonces, la intersección entre los nombres de los dos DFs serán los del DF más pequeño, es decir, "df_grades". Por esto, nuevamente se obtienen 875 filas.

2.1 Más visualizaciones [0.5 puntos]¶

Genere dos visualizaciones extras que encuentre interesantes (y no triviales) con estos datos y explique sus resultados. Agrupe los atributos que estime convenientes.

To-Do:

  • [ ] Generar dos nuevas visualizaciones con los datos y explicar que están representando.

NOTA: No utilice historia ni ciencias, son notas generadas aleatoriamente.

In [123]:
#melt del df_grades
df_grades_map_melt=df_grades.melt(id_vars=['test preparation course'])

# filtrado a solo mostrar notas de las materias
materias = ["math score","reading score","writing score"]
df_grades_map_melt_f = df_grades_map_melt[df_grades_map_melt.variable.isin(materias)]
#df_grades_map_melt_f
# boxplot
fig = px.box(df_grades_map_melt_f, x="variable", y="value", color="test preparation course") 
fig.show()

Justificación: Del gráfico generado, se puede apreciar que los que hacen test de preparación les va notablemente mejor en sus promedios, en las tres materias.

In [146]:
fig = px.scatter_matrix(df_grades, 
                        height=1000, 
                        dimensions=[
                            "math score",
                            "reading score",
                            "writing score",
                        ], 
                        symbol="gender",
                        color="gender"
                       )
fig.show()

Justificación: Se pueden obtener dos conclusiones principales de los gráficos:

  1. A los hombres les va mejor en matemáticas que en materias asociadas al lenguaje ("reading" y "writing"), y a las mujeres mejor en las de lenguaje que en matemáticas. Lo primero se ve en los gráficos superiores, donde se aprecia que los puntos rojos (hombres) se agrupan en la región superior de los gráficos, indicando una mejor nota de matemáticas dada una nota en "reading" o "writing". Por otro lado, en los gráficos de reading score y writing score versus math score (ubicados en el centro-izquierda y abajo-izquierda de la figura), se puede notar que las mujeres obtienen mejores resultados de "reading" o "writing", dada una nota en matemáticas.

  2. Del gráfico centro-derecho, se conluye que a los alumnos que les va bien en writing probablemente les va bien en reading también, dado que todos los puntos, sin importar el género, se ubican en la diagonal.

  3. Sin embargo, del mismo gráfico centro-derecho, se pueden notar que a las mujeres les va mejor en reading y Writing, dado que los puntos azules se acumulan en el lado superior de la diagonal, mientras que los rojos se agrupan en la región inferior de la misma diagonal.

Conclusión¶

Eso ha sido todo para el lab de hoy, recuerden que el laboratorio tiene un plazo de entrega de una semana y que los días de atraso no se pueden utilizar para entregas de lab solo para tareas. Cualquier duda del laboratorio, no duden en contactarnos por mail o U-cursos.



Created in deepnote.com Created in Deepnote